package com.shaokel.elasticsearchdemo;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.util.Maps;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexResponse;
import org.elasticsearch.cluster.metadata.MappingMetadata;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.MaxAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@SpringBootTest
public class IndexApi {
    /**
     * es 索引
     */
    public static final String index = "user";

    @Autowired
    private RestHighLevelClient client;

    @Test
    public void ping() throws IOException {
        if (client.ping(RequestOptions.DEFAULT)) {
            log.info("链接成功");
        } else {
            log.info("链接失败 !");
        }
    }

    /**
     * 创建索引
     */
    @Test
    public void createIndex() throws IOException {
        CreateIndexRequest request = new CreateIndexRequest(index);
        CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);

        // 创建索引 ===> {"acknowledged":true,"fragment":false,"shardsAcknowledged":true}
        log.info("创建索引 ===> " + JSONObject.toJSONString(createIndexResponse));

        //添加别名
        request.alias(new Alias("user_alias"));
        //索引设置
        request.settings(
                Settings.builder()
                        .put("index.number_of_shards", 3)
                        .put("index.number_of_replicas", 2)
        );

        // index mappings
        //        {
        //            "mapping": {
        //            "_doc": {
        //                "properties": {
        //                    "name": {
        //                        "type": "text"
        //                    }
        //                }
        //            }
        //        }
        //        }
        XContentBuilder builder = XContentFactory.jsonBuilder();
        builder.startObject();
        {
            builder.startObject("properties");
            {
                builder.startObject("name");
                {
                    builder.field("type", "text");
                }
                builder.endObject();
            }
            builder.endObject();
        }
        builder.endObject();
        request.mapping(builder);
        // 设置请求超时时间:
        request.setTimeout(TimeValue.timeValueMinutes(10));
    }

    /**
     * 新增索引--同样是创建索引
     */
    @Test
    public void addIndex() throws IOException {
        IndexRequest request = new IndexRequest();
        request.index(index).id("1101");

        Map<String, Object> userMap = new HashMap<>();
        userMap.put("name", "shaokel");
        userMap.put("sex", "男");
        userMap.put("age", 18);

        request.source(userMap);

        IndexResponse response = client.index(request, RequestOptions.DEFAULT);
        log.info(String.valueOf(response.getResult()));
    }

    /**
     * 修改文档
     *
     * @throws IOException
     */
    @Test
    public void updateIndex() throws IOException {
        UpdateRequest request = new UpdateRequest(index, "1001");
        // 设置请求体，对数据进行修改
        request.doc(XContentType.JSON, "sex", "女");
        UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
        log.info(String.valueOf(response.getResult()));
    }

    /**
     * 通过查询进行更新
     * @throws IOException
     */
    @Test
    public void updateByQuery() throws IOException {
//        UpdateByQueryRequest request = new UpdateByQueryRequest(index);
//        //选择性更新
//        request.setQuery(QueryBuilders.matchQuery("sex","女"));
//        request.setScript(new Script(ScriptType.INLINE, "painless", "ctx._source.times += params.count", Collections.singletonMap("count", 10)));
    }

    /**
     * 查询索引
     *
     * @throws IOException
     */
    @Test
    public void existIndex() throws IOException {
        GetIndexRequest request = new GetIndexRequest(index);
        boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
        log.info("索引{}存在 ===> {}", index, exists);

        GetIndexResponse response = client.indices().get(request, RequestOptions.DEFAULT);
        log.info("索引别名===>{}", response.getAliases());
        log.info("<===索引映射===>{}", response.getMappings());
        log.info("索引设置===>{}", response.getSettings());
    }

    /**
     * 查询索引2
     *
     * @throws IOException
     */
    @Test
    public void existIndex2() throws IOException {
        GetRequest request = new GetRequest(index, "1001");
        GetResponse response = client.get(request, RequestOptions.DEFAULT);
        log.info("{}", response.getSourceAsString());
    }

    /**
     * 异步查询索引
     *
     * @throws IOException
     */
    @Test
    public void fetchAsync() throws IOException {
        GetRequest request = new GetRequest(index, "1001");
        // 异步执行
        client.getAsync(request, RequestOptions.DEFAULT, new ActionListener<GetResponse>() {
            @Override
            public void onResponse(GetResponse getResponse) {
                log.info("异步回调>>>>");
                // 获取索引名称
                String index = getResponse.getIndex();
                log.info("index >>>> {}", index); // index >>>> req_log
            }

            @Override
            public void onFailure(Exception e) {
                log.error(e.getMessage());
            }
        });

        // 阻塞一下线程
        for (; ; ) {
        }
    }

    /**
     * 删除索引
     *
     * @throws IOException
     */
    @Test
    public void delIndex() throws IOException {
        DeleteIndexRequest request = new DeleteIndexRequest(index);
        AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
        log.info("删除索引 ===> {}", JSONObject.toJSONString(delete)); // 删除索引 ===> {"acknowledged":true,"fragment":false}
    }

    /**
     * 删除索引2
     *
     * @throws IOException
     */
    @Test
    public void delIndex2() throws IOException {
        DeleteRequest request = new DeleteRequest(index, "1001");
        DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
        log.info("删除索引 ===> {}", response.getResult()); // 删除索引 ===> {"acknowledged":true,"fragment":false}
    }

    /**
     * 批量新增
     *
     * @throws IOException
     */
    @Test
    public void bulkAddIndex() throws IOException {
        BulkRequest request = new BulkRequest();
        request.add(new IndexRequest(index).id("1002").source(XContentType.JSON, "name", "A1", "age", 37));
        request.add(new IndexRequest(index).id("1003").source(XContentType.JSON, "name", "B2", "age", 27));
        request.add(new IndexRequest(index).id("1004").source(XContentType.JSON, "name", "C3", "age", 17));
        //批量删除
//        request.add(new DeleteRequest(index).id("1004"));
        BulkResponse responses = client.bulk(request, RequestOptions.DEFAULT);
        System.out.println(responses.getTook());
        System.out.println(responses.getItems());
    }

    /**
     * 高级查询
     *
     * @throws IOException
     */
    @Test
    public void fullQuery() throws IOException {
        SearchRequest request = new SearchRequest(index);

        //全量
        SearchSourceBuilder builder = new SearchSourceBuilder().query(QueryBuilders.matchAllQuery());

        //条件查询
//        SearchSourceBuilder builder = new SearchSourceBuilder().query(QueryBuilders.termQuery("age", 19));

        //分页查询
//        SearchSourceBuilder builder = new SearchSourceBuilder().query(QueryBuilders.matchAllQuery());
        //起始页
//        builder.from(0);
        //一页的数量
//        builder.size(30);

        //排序
//        builder.sort("age", SortOrder.DESC);

        //字段过滤
//        String excludesc[] = {"age"};
//        String includesc[] = {};
//        query.fetchSource(includesc, excludesc);

        //组合查询
//        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //表示只要女的，不要男的
//        boolQueryBuilder.must(QueryBuilders.matchQuery("sex", "女"));
//        boolQueryBuilder.mustNot(QueryBuilders.matchQuery("sex", "男"));
        //表示只要30和40岁的
//        boolQueryBuilder.should(QueryBuilders.matchQuery("age", 18));
//        boolQueryBuilder.should(QueryBuilders.matchQuery("age", 40));
//        builder.query(boolQueryBuilder);

        //范围查询
//        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("age");
//        rangeQuery.gte(18);
//        rangeQuery.lte(28);
//        builder.query(rangeQuery);

        //模糊查询
//        FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("name","shoakel");
//        //Fuzziness.ONE表示相差的字符
//        fuzzyQueryBuilder.fuzziness(Fuzziness.ONE);
//        builder.query(fuzzyQueryBuilder);

        //高亮查询
//        TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("name", "shaokel");
//        HighlightBuilder highlightBuilder = new HighlightBuilder();
//        highlightBuilder.preTags("<font color='red'>");
//        highlightBuilder.postTags("</font");
//        highlightBuilder.field("name");
//        builder.highlighter(highlightBuilder);
//        builder.query(termsQueryBuilder);

        //最大值查询
//        AggregationBuilder aggregationBuilder = AggregationBuilders.max("maxAge").field("age");
        //分组查询
        AggregationBuilder aggregationBuilder = AggregationBuilders.terms("ageGroup").field("age");
        builder.aggregation(aggregationBuilder);

        request.source(builder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        System.out.println(response);

        SearchHits hits = response.getHits();
        System.out.println(hits.getTotalHits());
        System.out.println(response.getTook());
        System.out.println();
        for (SearchHit hit : hits) {
            System.out.println(hit.getSourceAsString());
            //打印高亮结果
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (!highlightFields.isEmpty()) {
                System.out.println(highlightFields);
            }
        }
    }
}